#pragma once

#include <queue>
#include <list>
#include <map>
#include <string>



/*-----------------------------------------
    Event Types
------------------------------------------*/
enum EventType
{
    EVT_MOVE    = (1<<0),   // entity has moved
    EVT_ATTACK  = (1<<1),   // entity is trying to attack
    EVT_USE     = (1<<2),   // entity is trying to 'use'
    EVT_THROW   = (1<<3),   // entity is throwing
    EVT_FACE    = (1<<4),   // entity changed face direction

    EVT_DIALOG  = (1<<5),
    EVT_ENTITY  = (1<<6),

    EVT_ADD_MISSION = (1<<7),           // mission holder adds
    EVT_RM_MISSION = (1<<8),            // mission holder removes
    EVT_START_MISSION = (1<<9),         // missiondispatcher sends a new mission
    EVT_FAILED_MISSION = (1<<10),       // missiondispatcher sends a failed status
    EVT_COMPLETED_MISSION = (1<<11),


    EVT_GUI_ACTIVATE = (1<<12),         // a new gamestate has been activated
    EVT_GUI_DEACTIVATE = (1<<13),        // a state has been deactivated
	
	EVT_ADD_ENTITY = (1<<14),
	EVT_RM_ENTITY = (1<<15),

    EVT_ADD_PARTICLE    = (1<<16),
    EVT_RM_PARTICLE     = (1<<17)

};

/*-----------------------------------------
    Listener Type
------------------------------------------*/
enum ListenerType
{
    LT_ENTITY              = (1<<0),    // Entity listener
    LT_DIALOG              = (1<<1),    // Dialog listener
    LT_MISSION_HOLDER      = (1<<2),    // Mission Holder
    LT_MISSION_DISPATCHER  = (1<<3),    // Mission Listener (dispatchs missions to a mission holder)
    LT_GUI                 = (1<<4),    // GUI listener - listen for gamestate activations
	LT_ADD_RM_ENTITY	   = (1<<5),	// Add/remove an entity
    LT_PARTICLEMNG         = (1<<6),    // Add/remove a particle
    LT_ALL    = 0xFFFFFFFF
};


/**
====================
The events Data
====================
*/
struct IEventData
{
    virtual IEventData* Copy() { return NULL; };
    virtual ~IEventData() {};
};

/**
=======================
The actual Event
=======================
*/
class IEvent
{
public:
    IEvent(EventType type, float time, IEventData* data, ListenerType listenerType) 
        :  m_type(type),
           m_time(time),
           m_data(data),
           m_Ltype(listenerType) {};

    IEvent(const IEvent &e) {
        m_type = e.m_type;
        m_time = e.m_time;
        m_data = NULL;
        m_data = e.m_data->Copy();
        m_Ltype = e.m_Ltype;
    };


    /* Get the Type of Event */
    int GetType() const { return m_type; };
    /* Get the Listener Type of Event */
    int GetListenerType() const { return m_Ltype; };
    /** Get the Time it was fired */
    float GetTimeFired() const { return m_time; };

    /** Get the data */
    template<typename T>
    T* GetData() { return reinterpret_cast<T*>(m_data); }; 

    /** Delete */
    virtual ~IEvent() { if ( m_data ) { delete m_data; m_data = NULL; } };
private:
    // listener type
    ListenerType m_Ltype;
    // Type of Event
    EventType   m_type;
    // time the event was fired
    float       m_time;
    // event data
    IEventData* m_data;

};



/**
=============================
The glue of the subsystems
Hopefully one of these days this will actually 
be true.  As of right now, I am only having player
actions recorded as events.  I am doing this so
we can have play back functionality.

Singleton
=============================
*/
class IEventListener;
class EventManager
{
public:
    /** Get the singleton */
    static EventManager* GetInstance() { return instance; };

    /** Add a Listener */
    void AddListener( IEventListener* listener );
    /** Remove a listener */
    void RemoveListener( IEventListener* listener);

    /** Trigger an event NOW */
    void TriggerEvent( IEvent &e );
    /** Update, trigger any events in the queue */
    void UpdateEventQueue( long deltaTime );

    /** Queue an Event */
    bool QueueEvent( IEvent* e );
    /** Abort an event */
    bool AbortQueuedEvent( const IEvent &e );
    /** Empty all events */
    void EmptyQueue( bool executeAllEvents = false );
    

private:
    EventManager(void);
    // our only instance
    static EventManager* instance;


    // list of listeners
    typedef std::list<IEventListener*> type_Listeners;
    // hash of listeners index
    typedef std::map<int, type_Listeners > type_ListTypes;
    type_ListTypes      m_listeners;

    // queue of events
    typedef std::queue< IEvent* > type_EventQueue;
    type_EventQueue     m_events;
public:
    // destroy
    virtual ~EventManager() { EmptyQueue( false ); };
};


/**
=============================
Event Listener-
Implement this interface, and
register with the EventManager
to receive events
=============================
*/
class IEventListener
{
public:
    IEventListener() { };//EventManager::GetInstance()->AddListener(this); };
    /* Get the Name of the Listener */
    virtual std::string GetName()=0;
    /** Get Listener Type - Events this listener listens too */
    virtual int GetListenerType()=0;

    /** Handle an event */
    virtual bool HandleEvent( IEvent* e )=0;

    virtual ~IEventListener() { };//EventManager::GetInstance()->RemoveListener(this); };
};



